package com.miaocat19.service.impl;

import com.miaocat19.dao.OrderDOMapper;
import com.miaocat19.dao.SequenceDOMapper;
import com.miaocat19.dao.StockLogDOMapper;
import com.miaocat19.dataobject.OrderDO;
import com.miaocat19.dataobject.SequenceDO;
import com.miaocat19.dataobject.StockLogDO;
import com.miaocat19.error.BusinessException;
import com.miaocat19.error.EmBusinessError;
import com.miaocat19.service.ItemService;
import com.miaocat19.service.OrderService;
import com.miaocat19.service.UserService;
import com.miaocat19.service.model.ItemModel;
import com.miaocat19.service.model.OrderModel;
import com.miaocat19.service.model.UserModel;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @program: Javamiaosha
 * @description: 1.这个地方我们整理下 下单的逻辑 因为确实是比较复杂的
 * <p>
 * 以后为了保证数据库的一致性 如果这个地方不分析好的话
 * 会更复杂的
 * <p>
 * (1)我们首先判断一下我们下单的商品和用户存不存在
 * (2)还要判断下 我们要不能下单零个 或者大于99个
 * (3)我们 这里就先不看有秒杀活动了 然后是落单减库存
 * (4)落单减库存
 * （5）插入订单信息
 * （6）增加商品的的销量
 * @author: miaocat19
 * @create: 2021-12-11 20:00
 **/
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private UserService userService;

    @Autowired
    private ItemService itemService;

    @Autowired
    private OrderDOMapper orderDOMapper;

    @Autowired
    private SequenceDOMapper sequenceDOMapper;

    @Autowired
    private StockLogDOMapper stockLogDOMapper;

    @Override
    @Transactional
    public OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount, String stockLogId) throws BusinessException {

        //1、校验下单的状态，下单的商品是不是存在，用户是不是合法，购买的数量是不是合适
//        ItemModel itemById = itemService.getItemById(itemId);


        /*做下优化我们在这里使用redis做一下缓存     减少数据库的压力*/
        ItemModel itemById = itemService.getItemByIdInCache(itemId);

        if (itemById == null) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "商品的信息不存在");
        }


        /*这个地方我们在上面做秒杀令牌的 时候已经做过了*/
        /*这个地方和上面的地方相同都需要做一下缓存*/
        //UserModel userModel = userService.getUserById(userId);
        /*UserModel userModel = userService.getUserByIdInCache(userId);

        if (userModel == null) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "用户不存在");
        }*/

        if (amount <= 0 || amount > 99) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "数量的信息不对");
        }

        /*这个地方我们也是在看是不是有秒杀令牌的时候做过了 */
        /*这个 地方还没有  秒杀的活动*/
        /*这个地方  是看是不是秒杀活动  如果是的话    */
        /*if (promoId != null) {
            if (promoId.intValue() != itemById.getPromoModel().getId()) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动的信息有问题");
            } else if (itemById.getPromoModel().getStatus().intValue() != 2) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动并没有开始");
            }
        }*/


        /*这个地方是在防止异步减库存 之后出现错误，所以我们可以等其他的操作做完之后在进行异步减库存*/
        /*2、这个地方是落单减库存*/
        boolean result = itemService.decreaseStock(itemId, amount);

        if (!result) {
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        }

        /*3、订单入库*/
        OrderModel orderModel = new OrderModel();
        orderModel.setUserId(userId);
        orderModel.setItemId(itemId);
        orderModel.setAmount(amount);
        orderModel.setPromoId(promoId);


        /*这个地方先不要管活动的事情*/
        if (promoId != null) {
            orderModel.setItemPrice(itemById.getPromoModel().getPromoItemPrice());
        } else {
            orderModel.setItemPrice(itemById.getPrice());
        }


        orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));

        //生成交易流水号,订单号
        orderModel.setId(generateOrderNo());

        OrderDO orderDO = convertFromOrderModel(orderModel);


        /*插入订单信息*/
        orderDOMapper.insertSelective(orderDO);

        //加上商品的销量
        itemService.increaseSale(itemId, amount);

        StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);

        if (stockLogDO == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        }

        stockLogDO.setStatus(2);
        stockLogDOMapper.updateByPrimaryKeySelective(stockLogDO);


        /*为了防止提交失败,我们可以在提交之后在进行异步减库存
         * 这个地方也就是使用了TransactionSynchronizationManager.registerSynchronization方法去在这个事务进行提交之后进行操作减库存
         * 但是这个地方我们还是存在意外 就是因为我们这里还是会出现当消息发送失败的时候
         * 我们
         * */

 /*       TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                boolean mqresult = itemService.asyncDecreaseStock(itemId, amount);
                *//*这里的意思是我们消息发送失败之后  我们上面的订单已经增加了
         * 如果我们发送失败消息
         * 上面的订单增加了 但是库存每减少呀
         * 这他妈也是个事情
         * 所以这种操作是不可行的
         * 所以我们需要 用到mq的事务进行处理
         *
         * *//*
//                if (!mqresult){
//                    itemService.increaseStock(itemId,amount);
//                    throw new BusinessException(EmBusinessError.MQ_SEND_FAIL);
//                }
            }
        });
*/

        return orderModel;
    }


    /*这里的作用是写出来  生成16位的订单号*/
    /*事务的表示就算回滚 也没有关系的  这个事务是一定执行的*/
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String generateOrderNo() {

        StringBuilder stringBuilder = new StringBuilder();
        /*//前8位为时间信息，年月日*/
        LocalDateTime now = LocalDateTime.now();
        String replace = now.format(DateTimeFormatter.ISO_DATE).replace("-", "");
        stringBuilder.append(replace);


        //中间6位为自增序列
        //获取当前sequence
        int sequence = 0;
        SequenceDO sequenceDO = sequenceDOMapper.getSequenceByName("order_info");
        sequence = sequenceDO.getCurrentValue();
        sequenceDO.setCurrentValue(sequenceDO.getCurrentValue() + sequenceDO.getStep());
        /**/
        sequenceDOMapper.updateByPrimaryKeySelective(sequenceDO);
        String sequenceStr = String.valueOf(sequence);
        for (int i = 0; i < 6 - sequenceStr.length(); i++) {
            stringBuilder.append(0);
        }

        stringBuilder.append(sequenceStr);


        /*一共十六位 现在把 他的后两位也写出来  最后两位是分库分表用的*/
        //展示写死
        stringBuilder.append("00");

        return stringBuilder.toString();
    }

    /*copy*/
    private OrderDO convertFromOrderModel(OrderModel orderModel) {
        if (orderModel == null) {
            return null;
        }
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderModel, orderDO);
        orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
        orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());
        return orderDO;
    }
}
